#!/usr/bin/env python 
# -*- coding: utf-8 -*-

"""
# @Time    : 2020/12/24 18:44
# @Author  : Wang Yang
# @File    : AutoTestLibrary.py
"""

import getopt
import sys
import time
import os
import shutil
import re
import json
import random
import logging
# need install
import configparser
from SSHLibrary.library import SSHLibrary


class AutoTestLibrary(SSHLibrary):

    def __init__(self, *args, **kwargs):
        super(AutoTestLibrary, self).__init__(*args, width=1000, **kwargs)
        self.prompt = ']$'
        self.login_no_password = False
        self.deploy_path = None
        self.autotest_path = None
        self.fisco_path = None
        self.java_sdk_demo_path = None
        self.console_path = None
        self.python_sdk_path = None
        self.cur_ip = None
        self.use_python = 'python'
        self.java_sdk_demo_url = "https://github.com/FISCO-BCOS/java-sdk-demo.git"
        self.console_url = "https://github.com/FISCO-BCOS/console.git"
        self.fisco_bcos_url = "https://github.com/FISCO-BCOS/FISCO-BCOS.git"
        self.python_sdk_url = "https://github.com/FISCO-BCOS/python-sdk.git"
        self.download_fisco_from_ci = "https://raw.githubusercontent.com/FISCO-BCOS/FISCO-BCOS/dev/tools/ci/" \
                                      "download_bin.sh"
        self.release_url = "https://github.com/FISCO-BCOS/FISCO-BCOS/releases/download"
        self.autotest_ini = "{}/autotest.ini".format(os.getcwd())
        self.arr_error_keywords = ["Received HTTP code 503", "ERROR", "No such file or directory", "ImportError",
                                   "Permission denied", "cannot access", "unable to access", "Authentication failure",
                                   "command not found", "Error:", "BUILD FAILED", "connection reset by peer",
                                   "Failed to"]
        logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(name)s- %(message)s')
        logging.getLogger(__name__)

    def set_use_python(self, p):
        self.use_python = p

    def set_prompt(self, p):
        self.prompt = p

    def init4tool(self, dir_deploy=None):
        # self.check_python_version()
        cur_path = self.linux_cmd("pwd")
        if dir_deploy:
            self.deploy_path = dir_deploy
            self.write("mkdir -p {}&&cd {}".format(dir_deploy, dir_deploy))
        else:
            self.deploy_path = cur_path
            self.write('')
        self.read_expect(self.prompt)
        logging.info("Deploy path is {}".format(self.deploy_path))
        self.autotest_path = "{}/autotest".format(self.deploy_path)
        logging.info("AutoTest path is {}".format(self.autotest_path))
        self.fisco_path = "{}/FISCO-BCOS".format(self.autotest_path)
        logging.info("Fisco-bcos path is {}".format(self.fisco_path))
        self.java_sdk_demo_path = "{}/java-sdk-demo".format(self.autotest_path)
        logging.info("Java sdk demo path is {}".format(self.java_sdk_demo_path))
        self.console_path = "{}/console".format(self.autotest_path)
        logging.info("Console path is {}".format(self.console_path))
        self.python_sdk_path = "{}/python-sdk".format(self.autotest_path)
        logging.info("Python sdk path is {}".format(self.python_sdk_path))
        logging.info("Autotest ini is {}".format(self.autotest_ini))

    def reset_autotest_ini(self):
        logging.info("Generate the autotest cfg {}".format(self.autotest_ini))
        with open(self.autotest_ini, "w+") as f:
            f.write('[AutoTest]')

    def _read(self, expected, timeout):
        output = ''
        logging.info("timeout is {}".format(str(timeout)))
        max_time = time.time() + int(timeout)
        while time.time() < max_time:
            output += self.current.read_char()
            if expected in output: return output
            # time.sleep(.00001)
        raise Exception("No match found for '{}' in {}s, Output:{}".format(expected, timeout, output))

    def read_expect(self, expected, loglevel=None, timeout=20):
        return self._read_and_log(loglevel, self._read, expected, timeout)

    def current_datetime(self, option=1):
        if option == 1:
            fmt = "%Y-%m-%d %H:%M:%S"
        else:
            fmt = "%Y%m%d%H%M%S"
        py_cmd = '{} -c "import time;print time.strftime(\'{}\', time.localtime())"'.format(self.use_python, fmt)
        output = self.linux_cmd(py_cmd)
        return output

    @staticmethod
    def timestamp():
        return str(int(time.time()))

    @staticmethod
    def rdm(a, b):
        # type: (int, int) -> str
        return str(random.randint(a, b))

    @staticmethod
    def hex2dec(str_hex):
        return str(int(re.sub(r'\W', '', str_hex).upper(), 16))

    @staticmethod
    def dec2hex(str_dec):
        base = [str(x) for x in range(10)] + [chr(x) for x in range(ord('A'), ord('A') + 6)]
        num = int(str_dec)
        mid = []
        while True:
            if num == 0: break
            num, rem = divmod(num, 16)
            mid.append(base[rem])
        return ''.join([str(x) for x in mid[::-1]])

    @staticmethod
    def use_key(str_input, key):
        # type: (str, str) -> list
        arr_find = re.findall(r'{}=(.*)'.format(key), str_input)  # type: list
        if not arr_find: arr_find = re.findall(r'{}"=(.*)'.format(key), str_input)
        if not arr_find: arr_find = re.findall(r'{}:(.*)'.format(key), str_input)
        if not arr_find: arr_find = re.findall(r'{}":(.*)'.format(key), str_input)
        if arr_find:
            return [x.strip() for x in arr_find]
        else:
            return ['NULL']

    @staticmethod
    def row_num(str_input, num, split=None):
        logging.info("Row list: {}".format(str(str_input.splitlines())))
        map_split = {1: '=', 2: ':'}
        logging.info("map_split:{}".format(map_split))
        row_value = re.sub(r'[,\'"{}]', '', str_input.splitlines()[num - 1])
        if not split:
            rst = row_value.strip().strip('\n')
        else:
            if split in map_split:
                if len(row_value.split(map_split[split])) >= 2:
                    rst = row_value.split(map_split[split])[1].strip().strip('\n')
                else:
                    raise Exception("Can not find this split [{}]".format(map_split[split]))
            else:
                raise Exception("Can not support this split [{}]".format(split))
        return rst

    def get_account(self, str_input, num):
        rst = self.row_num(str_input, num, 2)
        return re.sub(r'\x1b\[0m', '', rst)

    @staticmethod
    def get_json(text):
        try:
            match_obj = re.search(r'({.*})', text, re.S)
            if match_obj:
                json_obj = json.loads(match_obj.group(1))
            else:
                raise Exception("Load json failed")
        except Exception as e:
            raise Exception("Load json failed:{}".format(str(e)))
        return json_obj

    @staticmethod
    def parse_params(params):
        opts, args = getopt.getopt(params, "hc:d:i:b:r:f:m:p:ul:", ["ipconf=", "ext=", "iplist=", "mysql=",
                                                                    "channel=", "commands=", "dir=", "sql=",
                                                                    "netmap=", "rfisco=", "timeout="])
        logging.info("opts:{}".format(str(opts)))
        logging.info("args:{}".format(str(args)))
        return opts, args

    @staticmethod
    def copy_autotest_lib():
        src = r'D:\v_wbyangwang\PycharmProjects\MyProject\AutoTestLibrary.py'
        dst = './AutoTestLibrary.py'
        if not os.path.exists(dst):
            if os.path.exists(src): shutil.copy(src, dst)

    def check_python_version(self):
        major_version = self.linux_cmd('{} -c "import sys;print(sys.version_info.major)"'.format(self.use_python))
        logging.info("Now use python is {}, major version is {}".format(self.use_python, major_version))
        if not major_version or int(major_version) != 2: raise Exception("Python support version is 2")

    def go_su(self, su_user, su_password):
        if su_user:
            self.write('')
            self.read_expect(self.prompt)
            self.write("su - {}".format(su_user))
            res = self.read(delay=2)
            if "assword" in res:
                self.write(su_password)
            self.read_expect(self.prompt)
            whoami = self.linux_cmd("whoami")
            if whoami != su_user:
                raise Exception("Excute failed: su - {}".format(su_user))

    def wait_for_password(self, password, no_password=False):
        res = self.read(delay=2)
        while not res:
            time.sleep(1)
            res = self.read(delay=2)
            logging.info("Waiting...")
        logging.info("read:{}".format(res))
        if r'yes/no' in res:
            self.write('yes')
            if not no_password:
                self.read_expect("assword")
                self.write(password)
        elif 'assword' in res:
            if not no_password:
                self.write(password)
            else:
                self.write('')
        else:
            self.write('')
        logging.info("Password is {}".format(password))
        # if not no_password:
        #     logging.info("Begin to wait for entering password")
        #     self.read_expect("assword")
        #     self.write(password)
        #     logging.info("Password is {}".format(password))
        self.read_expect(self.prompt, timeout=18000)

    # @staticmethod
    # def set_encoding(encoding='utf-8'):
    #     reload(sys)
    #     sys.setdefaultencoding(encoding)

    def check_ssh_success(self, tgt_host_ip):
        cur_host_ip = self.get_host_ip()
        if cur_host_ip != tgt_host_ip:
            logging.info("cur_host_ip:{} != tgt_host_ip:{}".format(cur_host_ip, tgt_host_ip))
            raise Exception("ssh failed.")

    def login_use_token(self, jump_host, jump_user, host_ip, username, password, pin_password, dir_deploy=None,
                        su_user=None, su_password=None, no_password=False, jump_port=22):
        logging.info("{}{}".format("=" * 30, "Begin to login use token"))
        try:
            logging.info("Encoding:{}".format(sys.getdefaultencoding()))
            self.login_no_password = no_password
            self.open_connection(jump_host, jump_user, port=jump_port)
            self.login(username, pin_password, delay='1 seconds')
            self.write('')
            self.read_expect(self.prompt)
            self.write("ssh {}".format(host_ip))
            self.wait_for_password(password, no_password)
            self.check_ssh_success(host_ip)
            # self.read_expect(self.prompt)
            self.go_su(su_user, su_password)
            self.cur_ip = host_ip
            self.init4tool(dir_deploy)
        except Exception as e:
            raise Exception(str(e))

    def login_normal(self, host_ip, username, password, port=22, dir_deploy=None, su_user=None, su_password=None):
        try:
            logging.info("Encoding:{}".format(sys.getdefaultencoding()))
            self.open_connection(host_ip, port=port)
            self.login(username, password, delay='1 seconds')
            self.write('')
            self.read_expect(self.prompt)
            self.go_su(su_user, su_password)
            self.cur_ip = host_ip
            self.init4tool(dir_deploy)
        except Exception as e:
            raise Exception(str(e))

    def python_cmd(self, cmd):
        self.write('')
        self.read_expect(self.prompt)
        self.write(self.use_python)
        python_prompt = '>>>'
        # print [x.strip() for x in cmd.splitlines()]
        for c in [x.strip() for x in cmd.splitlines()]:
            self.read_expect(python_prompt)
            self.write(c)
        match_obj = re.search(r'(.*)>>>', self.read(delay=0.5), re.S)
        if match_obj:
            output = match_obj.group(1).strip()
        else:
            output = ''
        self.write('exit()')
        self.read_expect(self.prompt)
        return output

    def check_path_exist(self, path):
        check_rst = self.linux_cmd("{} -c \"import os;print(os.path.exists('{}'))\"".format(self.use_python, path))
        logging.info("Check path exist result is {}".format(check_rst))
        if check_rst == "True":
            return True
        else:
            return False

    def get_host_ip(self):
        c = "import socket;s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM);s.connect(('8.8.8.8', 80));" + \
            "print(s.getsockname()[0]);s.close()"
        cmd = '{} -c "{}"'.format(self.use_python, c)
        ip = self.linux_cmd(cmd)
        logging.info("Current host ip is {}".format(ip))
        return ip

    def start_stop_node(self, option, ip, which_one):
        if (not which_one) or which_one == "all":
            script_name = "{}/{}/{}_all.sh".format(self.autotest_path, ip, option)
        else:
            script_name = "{}/{}/{}/{}.sh".format(self.autotest_path, ip, which_one, option)
        retry_times = 0
        logging.info("script_name:{}".format(script_name))
        if self.check_path_exist(script_name):
            while True:
                output = self.linux_cmd("bash {}".format(script_name))
                # check result
                if option == "start" and ("success" in output or "is running" in output): break
                if option == "stop" and ("success" in output or "isn't running" in output): break
                if retry_times == 5: break
                time.sleep(1)
                logging.info("...Retry to execute {}".format(script_name))
                retry_times += 1
            if retry_times == 5: raise Exception("{} the fisco-bcos is failed".format(option))
        else:
            logging.info("The file {} is not exist, no need execute".format(script_name))

    def drop_db(self, ip, which_one):
        # drop database if db type is mysql
        logging.info("Begin to drop the database if db type is mysql of group")
        output = None
        if (not which_one) or which_one == "all":
            if self.check_path_exist("{}/{}".format(self.autotest_path, ip)):
                output = self.linux_cmd("find {}/{} -name 'group.*.ini'".format(self.autotest_path, ip))
        else:
            if self.check_path_exist("{}/{}/{}".format(self.autotest_path, ip, which_one)):
                output = self.linux_cmd("find {}/{}/{} -name 'group.*.ini'".format(self.autotest_path, ip, which_one))
        if output:
            for group_ini in output.splitlines():
                db_type = self.opt_cfg_on_linux(group_ini, 'get', 'storage', 'type')
                logging.info("Check {}".format(group_ini))
                if db_type == "mysql":
                    db_name = self.opt_cfg_on_linux(group_ini, 'get', 'storage', 'db_name')
                    db_username = self.opt_cfg_on_linux(group_ini, 'get', 'storage', 'db_username')
                    db_passwd = self.opt_cfg_on_linux(group_ini, 'get', 'storage', 'db_passwd')
                    self.linux_cmd("echo 'drop database {};' | mysql -u{} -p{} >/dev/null 2>&1".format(db_name,
                                                                                                       db_username,
                                                                                                       db_passwd))
                else:
                    logging.info("The db type is {}, no need drop mysql database".format(db_type))

    def del_node(self, ip, which_one):
        # remove data dir of node
        if (not which_one) or which_one == "all":
            output = self.linux_cmd("find {}/{} -name 'data'".format(self.autotest_path, ip))
            for dir_node_data in output.splitlines():
                self.linux_cmd("rm -rf {}".format(dir_node_data))
        else:
            self.linux_cmd("rm -rf {}/{}/{}/data".format(self.autotest_path, ip, which_one))
        self.drop_db(ip, which_one)

    def opt_node(self, option, ip, which_one=None):
        if option in ['start', 'stop']:
            self.start_stop_node(option, ip, which_one)
        elif option in ['del', 'delete']:
            self.del_node(ip, which_one)

    def go_ssh(self, ip, user, password, port=36000, no_password=False, default_path=None):
        self.write('')
        self.read_expect(self.prompt)
        self.write("ssh -p {} {}@{}".format(port, user, ip))
        self.wait_for_password(password, no_password)
        self.check_ssh_success(ip)
        # self.read_expect(self.prompt)
        if default_path:
            self.write('cd {}'.format(self.deploy_path))
        else:
            if self.deploy_path and self.deploy_path != '.':
                self.write('mkdir -p {}&&cd {}'.format(self.deploy_path, self.deploy_path))
            else:
                self.write('')
        self.read_expect(self.prompt)

    def go_scp(self, option, ip, user, password, local_path, remote_path, port=36000, no_password=False):
        self.write('')
        self.read_expect(self.prompt)
        if option == "get":
            self.write("scp -P{} {}@{}:{} {}".format(port, user, ip, remote_path, local_path))
        elif option == "getr":
            self.write("scp -P{} -r {}@{}:{} {}".format(port, user, ip, remote_path, local_path))
        elif option == "put":
            self.write("scp -P{} {} {}@{}:{}".format(port, local_path, user, ip, remote_path))
        elif option == "putr":
            self.write("scp -P{} -r {} {}@{}:{}".format(port, local_path, user, ip, remote_path))
        else:
            raise Exception("Not support this option {}".format(option))
        self.wait_for_password(password, no_password)
        # self.read_expect(self.prompt, timeout=60)
        self.write('')
        self.read_expect(self.prompt)

    def clean_cur(self, clean_what, ip):
        logging.info("-----Clean {} of {}".format(clean_what, ip))
        if (not clean_what) or clean_what == "all":
            self.opt_node('stop', ip)
            self.linux_cmd("[ -d autotest ]&&mv autotest autotest_{}".format(self.current_datetime(2)))
            self.linux_cmd("mkdir -p autotest")
        elif clean_what == "node":
            self.opt_node('stop', ip)
            self.linux_cmd("rm -rf {}/{}".format(self.autotest_path, ip))
        else:
            raise Exception("Can't support --ext={}".format(clean_what))

    @staticmethod
    def parse_ip_info(ip_info):
        if len(ip_info.split(',')) == 3:
            ip, user, password = ip_info.split(',')
            port = 36000
        else:
            ip, user, password, port = ip_info.split(',')
        return ip, user, password, port

    def clean(self, ip_list=None, ext=None, no_password=False):
        logging.info("{}{}".format("=" * 30, "Begin to clean"))
        clean_what = ext.split('|')[0].lower() if ext else 'all'
        if ip_list:
            if (not clean_what) or clean_what == "all":
                self.reset_autotest_ini()
                self.opt_autotest_ini('set', 'ip_list', ip_list)
            arr_ip_list = ip_list.split('|')
            for ip_info in arr_ip_list:
                ip, user, password, port = self.parse_ip_info(ip_info)
                self.go_ssh(ip, user, password, port, no_password)
                self.clean_cur(clean_what, ip)
                self.write('')
                self.read_expect(self.prompt)
                self.write('exit')
                self.read_expect(self.prompt)
                time.sleep(1)
        else:
            self.clean_cur(clean_what, self.cur_ip)

    def check_os(self):
        cmd = "import platform;print(platform.linux_distribution())"
        output = self.linux_cmd('{} -c "{}"'.format(self.use_python, cmd))
        if 'CentOS' in output:
            rst = 'CentOS'
        else:
            rst = 'Ubuntu'
        logging.info("Current OS is {}".format(rst))
        return rst

    def install_dependence_4_fisco(self):
        logging.info("Begin to install dependence for fisco-bcos")
        self.write('')
        self.read_expect(self.prompt)
        self.linux_cmd("sudo yum install -y epel-release centos-release-scl", timeout=1800)
        self.linux_cmd("sudo yum install -y openssl-devel openssl cmake3 gcc-c++ git flex patch bison gmp-static "
                       "devtoolset-7", timeout=1800)

    def sync_fisco_bcos(self, no_password=False):
        logging.info("Begin to sync fisco-bcos")
        ip_list = self.opt_autotest_ini('get', 'ip_list')
        for ip_info in ip_list.split('|'):
            ip, user, password, port = self.parse_ip_info(ip_info)
            local_path_fisco = "{}/build/bin/fisco-bcos".format(self.fisco_path)
            remote_path_fisco = "{}/{}/fisco-bcos".format(self.autotest_path, ip)
            self.go_scp('put', ip, user, password, local_path_fisco, remote_path_fisco, port, no_password)

    def install_fisco_bcos(self, install_flag=4, git_branch='master', release_version=None, remote=None,
                           update_flag=False, remote_no_password=False):
        logging.info("{}{}".format("=" * 30, "Begin to install fisco-bcos"))
        if install_flag in [1, 2]:
            # need git clone source code
            self.linux_cmd("rm -rf {}&&cd {}".format(self.fisco_path, self.autotest_path))
            self.linux_cmd("git clone {}".format(self.fisco_bcos_url), timeout=1800)
            self.linux_cmd("cd {}".format(self.fisco_path))
            self.linux_cmd("git checkout {} && sleep 3".format(git_branch))
            # self.linux_cmd("git pull", timeout=1800)
        else:
            # no need clone source code
            self.linux_cmd("mkdir -p autotest/FISCO-BCOS/build/bin&&mkdir -p autotest/FISCO-BCOS/tools")
        # change to build dir
        self.linux_cmd("cd {}/build".format(self.fisco_path))
        if install_flag in [2, 3]:
            self.write('')
            self.read_expect(self.prompt)
            # download fisco-bcos directly
            if not release_version:
                # download mini version from ci
                if not self.check_path_exist("download_bin.sh"):
                    self.linux_cmd("curl {} | bash /dev/stdin -m -b {}".format(self.download_fisco_from_ci, git_branch),
                                   timeout=1800)
                else:
                    self.linux_cmd("bash download_bin.sh -m -b {}".format(git_branch), timeout=1800)
                self.linux_cmd("mv bin/build_chain.sh ../tools")
            else:
                # dwonload from release url
                self.linux_cmd("rm -f fisco-bcos.tar.gz && rm -f build_chain.sh")
                self.linux_cmd("wget {}/v{}/build_chain.sh".format(self.release_url, release_version), timeout=300)
                self.linux_cmd("wget {}/v{}/fisco-bcos.tar.gz".format(self.release_url, release_version), timeout=1800)
                self.linux_cmd("mkdir -p bin&&tar -zxvf fisco-bcos.tar.gz -C bin/&&mv build_chain.sh ../tools")
        elif install_flag == 4:
            # use scp get the fisco-bcos
            if remote:
                self.write('')
                self.read_expect(self.prompt)
                ip = remote.split('|')[0]
                user = remote.split('|')[1]
                password = remote.split('|')[2]
                remote_fisco_path = remote.split('|')[3] + "/build/bin/bak/fisco-bcos"
                port = remote.split('|')[4] if len(remote.split('|')) == 5 else 36000
                remote_build_chain_path = remote.split('|')[3] + "/tools/build_chain.sh"
                local_fisco_path = self.fisco_path + "/" + "build/bin/fisco-bcos"
                local_build_chain_path = self.fisco_path + "/" + "/tools/build_chain.sh"
                if self.check_path_exist(local_fisco_path):
                    self.linux_cmd("cp {} {}_{}".format(local_fisco_path, local_fisco_path, self.current_datetime(2)))
                self.go_scp('get', ip, user, password, local_fisco_path, remote_fisco_path, port, remote_no_password)
                self.go_scp('get', ip, user, password, local_build_chain_path, remote_build_chain_path, port, remote_no_password)
        else:
            # compile by yourself
            # self.install_dependence_4_fisco()
            if self.check_os() == 'CentOS':
                self.linux_cmd("source /opt/rh/devtoolset-7/enable")
                self.linux_cmd("cmake3 ..", timeout=3600)
            else:
                self.linux_cmd("cmake ..", timeout=3600)
            self.linux_cmd("make -j4", timeout=3600)
        output = self.linux_cmd("cd bin&&chmod u+x fisco-bcos&&./fisco-bcos -v")
        if update_flag: self.sync_fisco_bcos()
        self.write('')
        self.read_expect(self.prompt)
        if output:
            return output.splitlines()[0].split(':')[1].strip().strip('\n')
        else:
            return None

    def build_ipconf(self, ipconf):
        if not ipconf: raise Exception("Prameter is empty:ipconf")
        v_arr_ipconf = ipconf.split('|')
        self.linux_cmd("rm -f ipconf")
        for one_ip in v_arr_ipconf:
            self.linux_cmd("echo '{}' >> ipconf".format(one_ip))
        self.linux_cmd("cat ipconf")
        return v_arr_ipconf[0].split(':')[0]

    def replace_net_map(self, netmap):
        if netmap:
            logging.info("Begin to replace ip of p2p")
            find_conf_ini = self.linux_cmd('find ./ -name "config.ini"')
            for config_ini in find_conf_ini.splitlines():
                for net_map in netmap.split(','):
                    old, new = net_map.split('=')
                    self.linux_cmd("sed -i 's/{}/{}/g' {}".format(old, new, config_ini))

    @staticmethod
    def opt_cfg(cfg_file, opt, section, key, value=''):
        logging.info("[{}][{}][{}]{}={}".format(cfg_file, opt, section, key, value))
        rst = ''
        try:
            cf = configparser.ConfigParser()
            cf.read(cfg_file)
            if opt == "get":
                rst = cf.get(section, key)  # type: str
                logging.info(">>>>>get value:[{}]{}={}".format(section, key, rst))
            elif opt == "set":
                cf.set(section, key, value)
                with open(cfg_file, "w+") as f:
                    cf.write(f)
                logging.info(">>>>>set value:[{}]{}={}".format(section, key, value))
            elif opt == "del":
                cf.remove_option(section, key)
                with open(cfg_file, "w+") as f:
                    cf.write(f)
                logging.info(">>>>>delete key:[{}]{}".format(section, key))
            else:
                raise Exception("Not support this operation {}".format(opt))
        except configparser.NoOptionError as e:
            raise Exception(str(e))
        except Exception as e:
            raise Exception(str(e))
        return rst

    def opt_autotest_ini(self, opt, key, value=''):
        return self.opt_cfg(self.autotest_ini, opt, 'AutoTest', key, value)

    def opt_cfg_on_linux(self, cfg_file, opt, section, key, value=''):
        output = ''
        get_comm = '''{} -c "
import configparser
cf = configparser.ConfigParser()
cf.read('{}')
print('>>>')
print(cf.get('{}', '{}'))
"'''.format(self.use_python, cfg_file, section, key)
        set_comm = '''{} -c "
import configparser
cf = configparser.ConfigParser()
cf.read('{}')
cf.set('{}', '{}', '{}')
with open('{}', 'w+') as f:
 cf.write(f)
print('>>>')
"'''.format(self.use_python, cfg_file, section, key, value, cfg_file)
        del_comm = '''{} -c "
import configparser
cf = configparser.ConfigParser()
cf.read('{}')
cf.remove_option('{}', '{}')
with open('{}', 'w+') as f:
 cf.write(f)
print('>>>')
"'''.format(self.use_python, cfg_file, section, key, cfg_file)
        if opt == "get":
            output = self.linux_cmd(get_comm)
            match_obj = re.search(r'.*".*>>>(.*)', output, re.S)
            if match_obj:
                output = match_obj.group(1).strip().strip('\n')
            else:
                output = ''
        elif opt == "set":
            self.linux_cmd(set_comm)
        elif opt == "del":
            self.linux_cmd(del_comm)
        return output

    def opt_cfg_for_fisco_bcos(self, cfg_name, opt, section, key, value=''):
        cfg_name_without_path = None
        if '/' in cfg_name:
            cfg_name_without_path = cfg_name.split('/')[-1:][0]
            logging.info("cfg_name_without_path:{}".format(cfg_name_without_path))
            go_path = '/'.join(cfg_name.split('/')[:-1])
            logging.info("go_path:{}".format(go_path))
            self.linux_cmd("cd {}".format(go_path))
        cfg_files = self.linux_cmd("find ./ -name '{}'".format(cfg_name_without_path))
        for cfg_file in cfg_files.splitlines():
            return self.opt_cfg_on_linux(cfg_file, opt, section, key, value)

    def set_value_for_mysql(self, mysql, group_ini, node_num, group_id):
        if mysql:
            logging.info("Begin to set value for mysql db type")
            ip, user, password, port, database = mysql.split(',')
            self.opt_cfg_on_linux(group_ini, 'set', 'storage', 'db_ip', ip)
            self.opt_cfg_on_linux(group_ini, 'set', 'storage', 'db_username', user)
            self.opt_cfg_on_linux(group_ini, 'set', 'storage', 'db_passwd', password)
            self.opt_cfg_on_linux(group_ini, 'set', 'storage', 'db_port', port)
            self.opt_cfg_on_linux(group_ini, 'set', 'storage', 'db_name',
                                  "{}_g{}_{}".format(node_num, group_id, self.current_datetime(2)))

    def save_value_for_node(self, protect_id, mysql):
        logging.info("The protect ip is {}".format(protect_id))
        self.opt_autotest_ini('set', 'protect_id', protect_id)
        find_group_ini = self.linux_cmd('find ./ -name "group.*.ini"')
        map_groupid_cnt = dict()
        map_groupid_cnt_p = dict()
        for group_ini in find_group_ini.splitlines():
            # node_name = group_ini.split('/')[2]
            # logging.info("group_ini: {}".format(group_ini))
            ip = group_ini.split('/')[1]
            # logging.info("ip: {}".format(ip))
            group_id = group_ini.split('/')[4].split('.')[1]
            # logging.info("group_id: {}".format(group_id))
            node_num = group_ini.split('/')[2].replace('node', '')
            # logging.info("node_num: {}".format(node_num))
            if os.path.exists("{}/node.nodeid".format('/'.join(group_ini.split('/')[:-1]))):
                node_id = self.linux_cmd("cat {}/node.nodeid".format('/'.join(group_ini.split('/')[:-1])))
            elif os.path.exists("{}/gmnode.nodeid".format('/'.join(group_ini.split('/')[:-1]))):
                node_id = self.linux_cmd("cat {}/gmnode.nodeid".format('/'.join(group_ini.split('/')[:-1])))
            else:
                raise Exception("Node id file is not exist, please check dir {}".format('/'.join(group_ini.split('/')[:-1])))
            # logging.info("node_id: {}".format(node_id))
            if protect_id == ip:
                # for protect ip
                if group_id not in map_groupid_cnt_p:
                    node_name_id = 1000
                else:
                    node_name_id = map_groupid_cnt_p[group_id] + 1
                map_groupid_cnt_p[group_id] = node_name_id
            else:
                if group_id not in map_groupid_cnt:
                    node_name_id = 1
                else:
                    node_name_id = map_groupid_cnt[group_id] + 1
                map_groupid_cnt[group_id] = node_name_id
            # logging.info("group_id|node_name_id:{}|{}".format(group_id, node_name_id))
            key = "g{}_node{}".format(group_id, node_name_id)
            self.opt_cfg(self.autotest_ini, 'set', 'AutoTest', key, node_id)
            self.opt_cfg(self.autotest_ini, 'set', 'AutoTest', key+"_ip", ip)
            self.set_value_for_mysql(mysql, group_ini, node_num, group_id)

    def build_chain(self, ipconf=None, ext=None, netmap=None, mysql=None):
        logging.info("{}{}".format("=" * 30, "Begin to build chain"))
        f_fisco_bcos_bin = self.fisco_path + "/build/bin/fisco-bcos"
        self.linux_cmd("cd {}/tools&&rm -rf nodes".format(self.fisco_path))
        self.reset_autotest_ini()
        protect_ip = self.build_ipconf(ipconf)
        self.linux_cmd("bash build_chain.sh -f ipconf -e {} {}".format(f_fisco_bcos_bin, ext))
        self.linux_cmd("cd nodes")
        self.replace_net_map(netmap)
        self.save_value_for_node(protect_ip, mysql)

    def trans_node(self, ip_list, no_password=False):
        logging.info("{}{}".format("=" * 30, "Begin to transfer node"))
        if not ip_list: raise Exception("Parameter is empty:ip_list")
        # clean the node dir before transfer
        self.clean(ip_list=ip_list, ext='node')
        for ip_info in ip_list.split('|'):
            ip, user, password, port = self.parse_ip_info(ip_info)
            local_path = '{}/tools/nodes/{}'.format(self.fisco_path, ip)
            remote_path = '{}/{}'.format(self.autotest_path, ip)
            self.go_scp("putr", ip, user, password, local_path, remote_path, port, no_password)
        self.opt_autotest_ini('set', 'ip_list', ip_list)

    def start_node(self, ip_list=None, ext=None, no_password=False):
        logging.info("{}{}".format("=" * 30, "Begin to start node"))
        if ip_list:
            for ip_info in ip_list.split('|'):
                ip, user, password, port = self.parse_ip_info(ip_info)
                self.go_ssh(ip, user, password, port, no_password)
                self.opt_node("start", ip, ext)
                self.write('')
                self.read_expect(self.prompt)
                self.write('exit')
                self.read_expect(self.prompt)
                time.sleep(1)
        else:
            self.opt_node("start", self.cur_ip, ext)

    def stop_node(self, ip_list=None, ext=None, no_password=False):
        logging.info("{}{}".format("=" * 30, "Begin to stop node"))
        if ip_list:
            for ip_info in ip_list.split('|'):
                ip, user, password, port = self.parse_ip_info(ip_info)
                self.go_ssh(ip, user, password, port, no_password)
                self.opt_node("stop", ip, ext)
                self.write('')
                self.read_expect(self.prompt)
                self.write('exit')
                self.read_expect(self.prompt)
                time.sleep(1)
        else:
            self.opt_node("stop", self.cur_ip, ext)

    def remove_node_data(self, ip_list=None, ext=None, no_password=False):
        logging.info("{}{}".format("=" * 30, "Begin to remove node data"))
        if ip_list:
            for ip_info in ip_list.split('|'):
                ip, user, password, port = self.parse_ip_info(ip_info)
                self.go_ssh(ip, user, password, port, no_password)
                self.opt_node("del", ip, ext)
                self.write('')
                self.read_expect(self.prompt)
                self.write('exit')
                self.read_expect(self.prompt)
                time.sleep(1)
        else:
            self.opt_node("del", self.cur_ip, ext)

    def get_node_dirs(self, ip, which_one):
        if (not which_one) or which_one == "all":
            str_node_dirs = self.linux_cmd("find {}/{} -type d -name 'node*'".format(self.autotest_path, ip))
        else:
            str_node_dirs = "{}/{}/{}".format(self.autotest_path, ip, which_one)
        return str_node_dirs

    def find_log_file(self, log_dir, check_times=1):
        if self.check_path_exist(log_dir):
            logging.info("The log dir is {}".format(log_dir))
            date_yyyymmddhh = self.current_datetime(2)[0:10]
            logging.info("date_yyyymmddhh:{}".format(date_yyyymmddhh))
            files = self.linux_cmd("find {} -name 'log_{}.*.log'".format(log_dir, date_yyyymmddhh))
            file_index = -1
            for log_file_path in files.splitlines():
                file_name = log_file_path.split('/')[-1:][0]
                if file_name.startswith("log_" + date_yyyymmddhh):
                    tmp_idx = int(file_name.split('.')[1])
                    if tmp_idx > file_index:
                        file_index = tmp_idx
            if file_index >= 0:
                return "{}/log_{}.{:0>2d}.log".format(log_dir, date_yyyymmddhh, file_index)
            else:
                if check_times == 1:
                    raise Exception("Can not find the newest log file in {}".format(log_dir))
                else:
                    time.sleep(1)
                    logging.info("...Retry find log file")
                    return self.find_log_file(log_dir, check_times - 1)
        else:
            raise Exception("The log dir {} is not exist".format(log_dir))

    def find_key_words(self, key_words, log_file_path):
        if key_words and log_file_path:
            logging.info("Find the key words <<{}>> in {}".format(key_words, log_file_path))
            grep_comm = "cat {}".format(log_file_path)
            for key_word in key_words.split('#'):
                grep_comm += "|grep -i -w -a '{}'".format(key_word)
            grep_comm += "|tail -1"
            output = self.linux_cmd(grep_comm)
            bool_rst = True if output else False
            return bool_rst, output
        else:
            raise Exception("Find key words failed")

    def check_ip_log(self, ip, which_one, key_words, interval_sec):
        logging.info("==========Now check ip is {}".format(ip))
        logging.info("which_one={} key_words={} interval_sec={}".format(which_one, key_words, interval_sec))
        str_node_dirs = self.get_node_dirs(ip, which_one)  # type:str
        bool_rst = True
        output1 = None
        for node_dir in str_node_dirs.splitlines():
            log_dir = node_dir + '/log'
            log_file_name = self.find_log_file(log_dir, 3)
            bool_rst_1, output1 = self.find_key_words(key_words, log_file_name)
            bool_rst_2 = True
            if interval_sec != "0":
                logging.info(">>>>>>>>>>After {} seconds will check again".format(interval_sec))
                time.sleep(float(interval_sec))
                log_file_name = self.find_log_file(log_dir, 3)
                bool_rst_2, output2 = self.find_key_words(key_words, log_file_name)
                if bool_rst_2 and output1 and output2 and output1.split('|')[1] != output2.split('|')[1]:
                    bool_rst_2 = True
                else:
                    bool_rst_2 = False
            logging.info("bool_rst = {}&{}&{}".format(str(bool_rst), str(bool_rst_1), str(bool_rst_2)))
            bool_rst = bool_rst & bool_rst_1 & bool_rst_2
            logging.info("=====>>>{}:{}".format(log_dir, str(bool_rst)))
        self.write('')
        self.read_expect(self.prompt)
        return bool_rst, output1

    def check_log(self, ip_list=None, ext=None, no_password=False):
        logging.info("{}{}".format("=" * 30, "Begin to check log"))
        if not ext: raise Exception("The ext parameter value is empty")
        which_one = ext.split('|')[0]
        key_words = ext.split('|')[1]
        interval_sec = ext.split('|')[2] if len(ext.split('|')) == 3 else '0'
        # if which_one == "" or which_one in ["all", "ALL", "All"]: which_one = "all"
        which_one = "all" if not which_one else which_one.lower()
        bool_rst = True
        output = None
        if ip_list:
            for ip_info in ip_list.split('|'):
                ip, user, password, port = self.parse_ip_info(ip_info)
                self.go_ssh(ip, user, password, port, no_password)
                tmp_bool_rst, output = self.check_ip_log(ip, which_one, key_words, interval_sec)
                bool_rst = bool_rst & tmp_bool_rst
                self.write('exit')
                self.read_expect(self.prompt)
                time.sleep(1)
        else:
            bool_rst, output = self.check_ip_log(self.cur_ip, which_one, key_words, interval_sec)
        return bool_rst, output

    def build_conf_4_sdk(self, dir_conf, peers):
        f_conf = dir_conf + "/config.toml"
        f_example_conf = dir_conf + "/config-example.toml"
        if not self.check_path_exist(f_conf):
            self.linux_cmd("cp {} {}".format(f_example_conf, f_conf))
        arr_channel = []
        channel = ""
        if peers and peers.lower() != 'all':
            # get ip and port from parameter
            arr_channel = peers.split(',')
        else:
            # get all the ip and port from nodes dir
            config_ini_files = self.linux_cmd("find {}/tools/nodes -name 'config.ini'".format(self.fisco_path))
            for config_ini in config_ini_files.splitlines():
                ip = config_ini.split('/')[-3:][0]
                channel_port = self.opt_cfg_on_linux(config_ini, 'get', 'rpc', 'channel_listen_port')
                if "{}:{}".format(ip, channel_port) not in arr_channel:
                    arr_channel.append("{}:{}".format(ip, channel_port))
        tmp_ip = None
        protect_id = None
        if not peers: protect_id = self.opt_autotest_ini('get', 'protect_id')
        for tmp_channel in arr_channel:
            if protect_id and protect_id not in tmp_channel: continue
            if channel == "":
                channel = r'\"{}\"'.format(tmp_channel)
                tmp_ip = tmp_channel.split(':')[0]
            else:
                channel += r', \"{}\"'.format(tmp_channel)
        logging.info("channel:"+channel)
        # copy crt key
        self.linux_cmd("cp -R {}/tools/nodes/{}/sdk/* {}".format(self.fisco_path, tmp_ip, dir_conf))
        # modify config.toml
        self.opt_cfg_on_linux(f_conf, 'set', 'network', 'peers', '[{}]'.format(channel))

    def get_from_remote(self, remote=None, remote_no_password=False):
        self.write('')
        self.read_expect(self.prompt)
        ip = remote.split('|')[0]
        user = remote.split('|')[1]
        password = remote.split('|')[2]
        remote_path = remote.split('|')[3]
        port = remote.split('|')[4] if len(remote.split('|')) == 5 else 36000
        loca_path = self.autotest_path
        self.go_scp('getr', ip, user, password, loca_path, remote_path, port, remote_no_password)

    def install_console(self, git_branch='master', peers=None, remote=None, remote_no_password=False):
        logging.info("{}{}".format("=" * 30, "Begin to install console"))
        self.write('')
        self.read_expect(self.prompt)
        self.linux_cmd("cd {}&&rm -rf console".format(self.autotest_path))
        if remote:
            self.get_from_remote(remote, remote_no_password)
        else:
            self.linux_cmd("git clone {}".format(self.console_url), timeout=1800)
        self.linux_cmd("cd {}".format(self.console_path))
        self.linux_cmd("git checkout {} && sleep 3".format(git_branch))
        # self.linux_cmd("git pull", timeout=1800)
        self.linux_cmd("chmod u+x gradlew&&./gradlew build -x test", timeout=1800)
        self.build_conf_4_sdk("{}/dist/conf".format(self.console_path), peers)
        self.write('')
        self.read_expect(self.prompt)

    def install_dependence_4_python_sdk(self):
        if self.check_os() == 'CentOS':
            self.linux_cmd("sudo yum install -y zlib-devel libffi-devel wget git", timeout=1800)
        else:
            self.linux_cmd("sudo apt install -y zlib1g-dev libffi6 libffi-dev wget git", timeout=1800)

    def install_pyenv_4_python_sdk(self):
        output = self.linux_cmd("pyenv versions", ignore_error=True)
        # no need install again if existing
        if "python-sdk" not in output: self.linux_cmd("bash init_env.sh -p", timeout=3600)
        # active
        self.linux_cmd("source ~/.bashrc")
        self.linux_cmd('eval "$(pyenv init -)" && eval "$(pyenv init --path)" && eval "$(pyenv virtualenv-init -)"')
        self.linux_cmd("pyenv activate python-sdk")
        self.linux_cmd("python -V")

    def init_python_sdk_config(self, peers=None, sm_crypto=False, sm_crypto_channel=False):
        self.linux_cmd("cp {}/client_config.py.template {}/client_config.py".format(self.python_sdk_path,
                                                                                    self.python_sdk_path))
        if not peers:
            channel_host = self.opt_autotest_ini("get", "protect_id")
            node_config_ini = "{}/tools/nodes/{}/node0/config.ini".format(self.fisco_path, channel_host)
            if self.check_path_exist(node_config_ini):
                channel_port = self.opt_cfg_on_linux(node_config_ini, "get", "rpc", "channel_listen_port")
            else:
                raise Exception("The file {} is not exist".format(node_config_ini))
        else:
            if len(peers.split(':')) == 2:
                channel_host = peers.split(':')[0]
                channel_port = peers.split(':')[1]
            else:
                raise Exception("Please check the peers format[{}]".format(peers))

        self.set_value_4_python_sdk_config("channel_host", '"{}"'.format(channel_host))
        self.set_value_4_python_sdk_config("channel_port", channel_port)
        self.linux_cmd("cp -R {}/tools/nodes/{}/sdk/* {}/bin".format(self.fisco_path, channel_host, self.python_sdk_path))
        if sm_crypto:
            self.set_value_4_python_sdk_config("crypto_type", '"GM"')
            if sm_crypto_channel:
                self.set_value_4_python_sdk_config("channel_ca", '"bin\/gm\/gmca.crt"')
                self.set_value_4_python_sdk_config("channel_node_cert", '"bin\/gm\/gmsdk.crt"')
                self.set_value_4_python_sdk_config("channel_node_key", '"bin\/gm\/gmsdk.key"')
                self.set_value_4_python_sdk_config("channel_en_crt", '"bin\/gm\/gmensdk.crt"')
                self.set_value_4_python_sdk_config("channel_en_key", '"bin\/gm\/gmensdk.key"')
            else:
                self.set_value_4_python_sdk_config("ssl_type", '"ECDSA"')

    def set_value_4_python_sdk_config(self, k, v):
        cfg = "{}/client_config.py".format(self.python_sdk_path)
        self.linux_cmd(r"sed -i 's/{} =.*$/{} = {}/' {}".format(k, k, v, cfg))

    def install_python_sdk(self, git_branch='master', remote=None, remote_no_password=False, peers=None,
                           sm_crypto=False, sm_crypto_channel=False):
        logging.info("{}{}".format("=" * 30, "Begin to install python sdk"))
        self.write('')
        self.read_expect(self.prompt)
        # self.install_dependence_4_python_sdk()
        self.linux_cmd("cd {}&&rm -rf python-sdk".format(self.autotest_path))
        if remote:
            self.get_from_remote(remote, remote_no_password)
        else:
            self.linux_cmd("git clone {}".format(self.python_sdk_url), timeout=1800)
        self.linux_cmd("cd {}".format(self.python_sdk_path))
        self.linux_cmd("git checkout {} && sleep 3".format(git_branch))
        self.linux_cmd("sed -i 's/pyenv init -)/pyenv init --path)/' init_env.sh")
        # self.linux_cmd("git pull", timeout=1800)
        self.install_pyenv_4_python_sdk()
        self.linux_cmd("pip install --upgrade pip -i https://pypi.tuna.tsinghua.edu.cn/simple", timeout=1800)
        self.linux_cmd("pip install -i https://pypi.douban.com/simple  --trust -host=pypi.douban.com -r "
                       "requirements.txt", timeout=1800)
        if not remote: self.linux_cmd("bash init_env.sh -i", timeout=1800)
        self.init_python_sdk_config(peers, sm_crypto, sm_crypto_channel)
        self.linux_cmd("./console.py getNodeVersion")

    def install_java_sdk_demo(self, git_branch='main', peers=None, remote=None, remote_no_password=False):
        logging.info("{}{}".format("=" * 30, "Begin to install java sdk demo"))
        self.write('')
        self.read_expect(self.prompt)
        self.linux_cmd("cd {}&&rm -rf java-sdk-demo".format(self.autotest_path))
        if remote:
            self.get_from_remote(remote, remote_no_password)
        else:
            self.linux_cmd("git clone {}".format(self.java_sdk_demo_url), timeout=1800)
        self.linux_cmd("cd {}".format(self.java_sdk_demo_path))
        self.linux_cmd("git checkout {} && sleep 3".format(git_branch))
        # self.linux_cmd("git pull", timeout=1800)
        self.linux_cmd("chmod u+x gradlew&&./gradlew build -x test", timeout=1800)
        self.build_conf_4_sdk("{}/dist/conf".format(self.java_sdk_demo_path), peers)
        self.write('')
        self.read_expect(self.prompt)

    @staticmethod
    def prepare_commands(str_commands):
        arr_step = str_commands.split('#')
        arr_after_do = []
        for step in arr_step:
            map_after_do = dict()
            # command = replace_value(step.split('->')[0])  # type: str
            command = step.split('->')[0]  # type: str
            do = None
            if '->' in step:
                do = step.split('->')[1]
            map_after_do[command] = do
            arr_after_do.append(map_after_do)
        logging.info("arr_after_do:"+str(arr_after_do))
        return arr_after_do

    def replace_value(self, instring):
        if instring:
            logging.info("Before replace>>>>>{}".format(instring))
            # replace dq() to "
            instring = re.sub(r'dq\(\)', '"', instring)
            # replace q() to '
            instring = re.sub(r'q\(\)', "'", instring)
            match_obj = re.search(r'.*?var{(.*?)}.*', instring)
            # get value add double quotation
            match_obj_dq = re.search(r'.*?vardq{(.*?)}.*', instring)
            if match_obj or match_obj_dq:
                key = match_obj.group(1) if match_obj else match_obj_dq.group(1)
                if match_obj:
                    value = self.opt_cfg(self.autotest_ini, 'get', 'AutoTest', key)
                else:
                    value = '"' + self.opt_cfg(self.autotest_ini, 'get', 'AutoTest', key) + '"'
                if value:
                    # replace value
                    rst = re.sub('var{' + key + '}', value, instring) if match_obj else re.sub('vardq{' + key + '}',
                                                                                               value, instring)
                    return self.replace_value(rst)
                else:
                    # can not find the key from autotest/autotest.ini
                    logging.warning("Can not find the key [{}] from autotest.ini".format(key))
                    return instring
            else:
                logging.info("After replace>>>>>{}".format(instring))
                return instring
        else:
            return ''

    def process_after_do(self, output, str_do):
        local_vars = locals()
        try:
            if str_do:
                for do in str_do.split(';'):
                    do = self.replace_value(do)
                    match_obj = re.search(r'(.*?)=(.*)', do)
                    if match_obj:
                        # store key = value
                        key = match_obj.group(1).strip()
                        value = match_obj.group(2).strip()
                        logging.info("value is {}".format(value))
                        logging.info("eval(value)")
                        value_rst = eval(value)
                        local_vars[key] = value_rst
                        self.opt_cfg(self.autotest_ini, 'set', 'AutoTest', key, value_rst)
                    else:
                        # other process
                        logging.info("eval('{}')".format(do))
                        eval(do)
        except Exception as e:
            raise Exception(str(e))

    def execute_console_command(self, cmds, ext='', exec_dir=None, timeout=60):
        logging.info("{}{}".format("=" * 30, "Begin to execute console command"))
        if not cmds: raise Exception("Prameter is empty:cmds")
        rst = ""
        p = ']>'
        # hint_a = r'\x1b\[\?1l\x1b>\x1b\[\?2004l'
        # hint_b = r'\x1b\[\?1h\x1b=\x1b\[\?2004h'
        if exec_dir:
            self.linux_cmd("cd {}".format(exec_dir))
        else:
            self.linux_cmd("cd {}/dist".format(self.console_path))
        self.write("bash start.sh 1 {} -l".format(ext))
        start_output = self.read(delay=1)
        logging.info("start_output:{}".format(start_output))
        # if 'assword' in start_output:
        #     # for p12 account
        #     self.write('123456')
        #     self.read_expect(p)
        #     start_output = ''
        # if (not start_output) or (p in start_output):
        if not start_output:
            # step_idx = 1
            self.read_expect(p)
            arr_after_do = self.prepare_commands(cmds)  # type: list
            try:
                for map_after_do in arr_after_do:  # type: dict
                    command = list(map_after_do.keys())[0]  # type: str
                    real_command = self.replace_value(command)
                    logging.info("real_command:{}".format(real_command))
                    self.write('')
                    self.read_expect(p)
                    self.write(real_command)
                    if re.search(r'loadAccount (.*) p12', real_command):
                        # p12 account need enter password
                        res = self.read(delay=0.5)
                        if 'assword:' in res: self.write('123456')
                    command_output = self.read_expect(p, timeout=timeout)
                    logging.info("raw command_output:{}".format(command_output))
                    # match_obj = re.search(r'(.*)\[(.*)\]>', command_output, re.S)
                    match_obj = re.search(r'(.*)\[group:\d+\]>', command_output, re.S)
                    command_output = match_obj.group(1).strip().strip('\n') if match_obj else ''
                    logging.info("command_output:{}".format(command_output))
                    self.process_after_do(command_output, map_after_do[command])
                    # if os.path.exists("{}.output"): os.remove("{}.output")
                    # with open("{}.output".format(step_idx), "w") as f:
                    #     f.write(command_output)
                    # step_idx += 1
                    rst += "{}\n".format(command_output)
                else:
                    self.write('')
                    self.read_expect(p)
                    self.write('q')
                    self.read_expect(self.prompt)
            except Exception as e:
                raise Exception(str(e))
        else:
            # abnormal for starting
            rst = start_output.strip()
            logging.info("abnormal command_output:{}".format(rst))
        return rst

    def linux_cmd(self, cmds, timeout=10, interact_input=None, su_user=None, su_password=None, ignore_error=False):
        self.write('')
        self.read_expect(self.prompt)
        self.go_su(su_user, su_password)
        self.write(cmds)
        if interact_input:
            for i in interact_input.split('|'):
                expect, w = i.split(',')
                self.read_expect(expect)
                self.write(w)
        tmp_output = self.read_expect(self.prompt, timeout=timeout)
        # logging.info("tmp_output:{}".format(tmp_output))
        # match_obj = re.search(r'(.*)\[(.*?)\]\$', tmp_output, re.S)
        p = self.prompt
        if self.prompt.endswith('$'): p = p.replace('$', r'\$')
        match_obj = re.search(r'(.*)\n.*{}'.format(p), tmp_output, re.S)
        # print(match_obj)
        if match_obj:
            output = match_obj.group(1).strip().strip('\n')
            if not ignore_error:
                for err in self.arr_error_keywords:
                    if err in output: raise Exception("Execute cmd failed")
        else:
            output = ''
        # logging.info("linux_cmd output: ".format(output))
        # logging.info("----------------------")
        return output

    def execute_linux_command(self, cmds, exec_dir=None, timeout=20, interact_input=None, loop_times=1, su_user=None,
                              su_password=None, ignore_error=False):
        logging.info("{}{}".format("=" * 30, "Begin to execute linux command"))
        logging.info("Total loop times is {}".format(str(loop_times)))
        if not cmds: raise Exception("Prameter is empty:cmds")
        self.go_su(su_user, su_password)
        if exec_dir: self.linux_cmd("cd {}".format(exec_dir))
        rst = ""
        arr_after_do = self.prepare_commands(cmds)  # type: list
        self.write('')
        self.read_expect(self.prompt)
        try:
            for map_after_do in arr_after_do:  # type: dict
                command_output = ''
                command = list(map_after_do.keys())[0]  # type: str
                real_command = self.replace_value(command)
                logging.info("real_command:{}".format(real_command))
                for t in range(loop_times):
                    logging.info("Current loop times: {}...".format(str(t+1)))
                    command_output = self.linux_cmd(real_command, timeout=timeout, interact_input=interact_input,
                                                    ignore_error=ignore_error)
                    self.write('')
                    self.read_expect(self.prompt)
                logging.info("command_output:{}".format(command_output))
                self.process_after_do(command_output, map_after_do[command])
                rst += "{}\n".format(command_output)
            else:
                self.write('')
                self.read_expect(self.prompt)
        except Exception as e:
            raise Exception(str(e))
        return rst

    def execute_mysql(self, mysql_info, sql, opt='select'):
        if not mysql_info: raise Exception("Prameter is empty:mysql_info")
        if not sql: raise Exception("Prameter is empty:sql")
        # print(type(sql))
        ip, user, password, port, database = mysql_info.split(',')
        if opt == 'select' and 'select' in sql:
            sql = sql.replace('"', r'\\\"')
            py_cmd = '''{} -c "
import pymysql
db = pymysql.Connect(host='{}', user='{}', passwd='{}', db='{}', port={})
cursor = db.cursor()
cursor.execute(\\"{}\\")
table = cursor.fetchall()
rst = ''
for row in table:
 new_row = [str(x) for x in row]
 rst += '|'.join(new_row) + '!'
db.close()
print(rst)
    "'''.format(self.use_python, ip, user, password, database, port, sql)
            output = self.linux_cmd(py_cmd)
        else:
            raise Exception("Not support")
        match_obj = re.search(r'.*"(.*)', output, re.S)
        if match_obj:
            output = match_obj.group(1).strip().strip('\n')
        else:
            output = ''
        return output

    def close_autotest_connection(self):
        self.close_connection()


if __name__ == "__main__":
    try:
        # ssh = AutoTestLibrary()
        # ssh.close_connection()
        pass
    except Exception as ee:
        print("ERROR:{}".format(str(ee)))
